package com.cloudfun.campusshare.dao;

import com.cloudfun.campusshare.common.constant.Code;
import com.cloudfun.campusshare.common.model.dto.DeviceQueryDTO;
import com.cloudfun.campusshare.common.model.vo.DeviceListVO;
import com.cloudfun.campusshare.common.model.vo.DeviceStatisticsVo;
import com.cloudfun.campusshare.common.response.BaseResult;
import com.cloudfun.campusshare.common.response.BaseResultFactory;
import com.cloudfun.campusshare.common.response.PageResult;
import com.cloudfun.campusshare.common.security.AuthenticationHolder;
import com.cloudfun.campusshare.entity.DeviceEntity;
import com.cloudfun.campusshare.entity.ProjectEntity;
import com.cloudfun.campusshare.repo.DeviceEntityRepo;
import com.cloudfun.campusshare.repo.WashPointConcretePositionEntityRepo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author linyangping
 * @date 2019/11/6
 */
@Repository
public class DeviceDao {

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private DeviceEntityRepo deviceEntityRepo;

    @Resource
    private WashPointConcretePositionEntityRepo washPointConcretePositionEntityRepo;

    public PageResult<DeviceListVO> findByCondition(Pageable pageable, DeviceQueryDTO deviceQueryDTO) {
        pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(Sort.Direction.DESC, "firstInteractionTime"));
        Specification<DeviceEntity> specification = this.getSpec(deviceQueryDTO);
        Page<DeviceEntity> entityPage = deviceEntityRepo.findAll(specification, pageable);
        long totalElements = entityPage.getTotalElements();

        PageResult<DeviceListVO> result = new PageResult<>();
        result.setElements(this.toDeviceListVO(entityPage.getContent()));
        result.setTotalSize(totalElements);
        return result;
    }

    public List<DeviceListVO> toDeviceListVO(List<DeviceEntity> deviceEntities) {
        return deviceEntities.stream().map(this::toDeviceListVO).collect(Collectors.toList());
    }

    public DeviceListVO toDeviceListVO(DeviceEntity deviceEntity) {
        if (null == deviceEntity) {
            return null;
        }
        DeviceListVO vo = new DeviceListVO();
        BeanUtils.copyProperties(deviceEntity, vo);
        ProjectEntity project = deviceEntity.getProject();
        if (null != project) {
            vo.setProjectId(project.getId());
            vo.setProjectName(project.getName());
        }
        vo.setDeviceAddress(parseConcretePosition(deviceEntity));
        return vo;
    }

    public String parseConcretePosition(DeviceEntity deviceEntity) {
        if (StringUtils.isBlank(deviceEntity.getConcretePositionId())) {
            return null;
        }
        return washPointConcretePositionEntityRepo.findById(deviceEntity.getConcretePositionId())
                .map(entity -> entity.getBuilding() + "号楼" + entity.getFloor() + "层" + entity.getRoom() + "室").orElse(null);
    }


    public Specification<DeviceEntity> getSpec(DeviceQueryDTO deviceQueryDTO) {
        return (root, query, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();

            if (AuthenticationHolder.isAdmin()) {
                // 管理员查询
                if (StringUtils.isNotBlank(deviceQueryDTO.getUserId())) {
                    predicateList.add(criteriaBuilder.equal(root.get("userId"), deviceQueryDTO.getUserId()));
                }
            } else if (AuthenticationHolder.isFinancialInstitutions()) {
                //如果是金融机构
                if (StringUtils.isNotBlank(deviceQueryDTO.getUserId())) {
                    //按理还要判断该商户与金融机构是否有贷款项目关联，先不做
                    predicateList.add(criteriaBuilder.equal(root.get("userId"), deviceQueryDTO.getUserId()));
                }
            } else {
                // 商家
                predicateList.add(criteriaBuilder.equal(root.get("userId"), AuthenticationHolder.getUserId()));
            }
            if (StringUtils.isNotBlank(deviceQueryDTO.getValue())) {
                List<Predicate> like = new ArrayList<>();
                like.add(criteriaBuilder.like(root.get("id"), "%" + deviceQueryDTO.getValue() + "%"));
                like.add(criteriaBuilder.like(root.get("deviceName"), "%" + deviceQueryDTO.getValue() + "%"));
                like.add(criteriaBuilder.like(root.get("deviceNumber"), "%" + deviceQueryDTO.getValue() + "%"));
                like.add(criteriaBuilder.like(root.get("devicePlace"), "%" + deviceQueryDTO.getValue() + "%"));
                like.add(criteriaBuilder.like(root.get("deviceType"), "%" + deviceQueryDTO.getValue() + "%"));
                like.add(criteriaBuilder.like(root.get("address"), "%" + deviceQueryDTO.getValue() + "%"));
                like.add(criteriaBuilder.like(root.get("imei"), "%" + deviceQueryDTO.getValue() + "%"));

                // 项目
                Join<DeviceEntity, ProjectEntity> projectJoin = root.join(root.getModel().getSingularAttribute("project", ProjectEntity.class), JoinType.LEFT);
                like.add(criteriaBuilder.like(projectJoin.get("name"), "%" + deviceQueryDTO.getValue() + "%"));

                Predicate[] predicates = new Predicate[like.size()];
                predicateList.add(criteriaBuilder.or(like.toArray(predicates)));
            }

            if (!StringUtils.isBlank(deviceQueryDTO.getProjectId())) {
                predicateList.add(criteriaBuilder.equal(root.get("projectId"), deviceQueryDTO.getProjectId()));
            }
            if (null != deviceQueryDTO.getOnline()) {
                predicateList.add(criteriaBuilder.equal(root.get("online"), deviceQueryDTO.getOnline()));
            }

            Predicate[] predicates = new Predicate[predicateList.size()];
            predicateList.toArray(predicates);
            return criteriaBuilder.and(predicates);
        };
    }

    public BaseResult<PageResult<DeviceStatisticsVo>> groupCountByUser(Pageable pageable, String value) {
        StringBuilder pageSqlBuilder = new StringBuilder("select u.id,u.username,count(d.id) from user u left join device d on d.user_id=u.id ");
        if (StringUtils.isNotBlank(value)) {
            pageSqlBuilder.append(" where u.username like :value");
        }
        pageSqlBuilder.append(" group by u.id,u.username order by count(d.id) desc limit :start,:end");
        StringBuilder totalNumSqlBuilder = new StringBuilder("select count(*) from (select u.id,u.username,count(d.id) from user u left join device d on d.user_id=u.id ");
        if (StringUtils.isNotBlank(value)) {
            totalNumSqlBuilder.append(" where u.username like :value");
        }
        totalNumSqlBuilder.append(" group by u.id,u.username) as a");
        Query pageQuery = entityManager.createNativeQuery(pageSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            pageQuery.setParameter("value", "%" + value + "%");
        }
        pageQuery.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
        pageQuery.setParameter("end", pageable.getPageSize());

        List<Object[]> resultList = pageQuery.getResultList();

        List<DeviceStatisticsVo> statisticsVos = resultList.stream().map(tuple -> {
            String userId = (String) tuple[0];
            String username = (String) tuple[1];
            BigInteger count = (BigInteger) tuple[2];
            DeviceStatisticsVo statisticsVo = new DeviceStatisticsVo();
            statisticsVo.setUserId(userId);
            statisticsVo.setUsername(username);
            statisticsVo.setDeviceCount(count.longValue());
            return statisticsVo;
        }).collect(Collectors.toList());

        PageResult<DeviceStatisticsVo> pageResult = new PageResult<>();
        pageResult.setElements(statisticsVos);

        Query totalNumQuery = entityManager.createNativeQuery(totalNumSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            totalNumQuery.setParameter("value", "%" + value + "%");
        }
        List<BigInteger> countResList = totalNumQuery.getResultList();
        pageResult.setTotalSize(countResList.get(0).longValue());
        return BaseResultFactory.produceResult(Code.SUCCESS, pageResult);
    }

}
